/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.gmu.javalution.pfronts;

import java.beans.SimpleBeanInfo;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author Jason
 */
public class DebParetoFronts<VEC, VEC_COMPARER extends Comparator<VEC>>
        implements IParetoFront<VEC> {

    private boolean dirty = false;
    private final Map<Integer, Collection<VEC>> front2vecs =
            new TreeMap<Integer, Collection<VEC>>();
    private final Map<VEC, Integer> vec2front = new HashMap<VEC, Integer>();
    private final List<VEC> vectors = new ArrayList<VEC>();
    private final int maxNumFronts;
    private final VEC_COMPARER comparer;

    public DebParetoFronts(VEC_COMPARER comparer, int maxNumFronts) {
        this.maxNumFronts = maxNumFronts;
        this.comparer = comparer;
    }

    @Override
    public void add(VEC vector) {
        dirty = true;
        vectors.add(vector);
    }

    @Override
    public Collection<VEC> getFront(int index) {
        if (dirty) {
            paretoSort();
            dirty = false;
        }

        return front2vecs.get(index);
    }

    @Override
    public int getNumberOfFronts() {
        if (dirty) {
            paretoSort();
            dirty = false;
        }
        return front2vecs.size();
    }

    public String toString2() {
        if (dirty) {
            paretoSort();
            dirty = false;
        }
        String s = "";
        int frontIndex = 0;
        for (Integer rank : front2vecs.keySet()) {
            Collection<VEC> front = front2vecs.get(rank);
            s += (frontIndex++) + "] => [";
            for (VEC vec : front) {
                if (vec instanceof double[]) {
                    s += Arrays.toString(((double[]) vec)) + '\t';
                } else {
                    s += vec.toString();
                }
            }

            s = s.trim();
            s += "]\n";
        }
        return s;
    }

    @Override
    public String toString() {
        if (dirty) {
            paretoSort();
            dirty = false;
        }

        StringBuffer fronteira = new StringBuffer();

        for (Integer rank : front2vecs.keySet()) {
            Collection<VEC> front = front2vecs.get(rank);
            for (VEC vec : front) {
                fronteira.append(Arrays.toString(((double[]) vec)) + "\n");
            }
            fronteira.append("\n");
        }
        return fronteira.toString();
    }

    private void paretoSort() {
        front2vecs.clear();
        vec2front.clear();

        //	Mark number unranked
        int numUnranked = vectors.size();

        //	Mark the rank we are currently assigning.
        int curRank = 0;

        //	A temporary array.
        List<VEC> temp = new ArrayList<VEC>();

        while (numUnranked > 0 && curRank <= maxNumFronts - 1) {
            //	Container for the current front
            List<VEC> front = new ArrayList<VEC>();

            for (VEC ind : vectors) {
                if (!vec2front.containsKey(ind) || vec2front.get(ind) == -1) {
                    //	If individual's rank has not been
                    //	assigned, then set it to current rank
                    //	and try it out.
                    vec2front.put(ind, curRank);
                    front.add(ind);

                    //	Now iterate over all candidates,
                    //	checking the new guy for dominance
                    boolean b_break = false;
                    for (Iterator<VEC> f = front.iterator(); f.hasNext();) {
                        VEC tf = f.next();

                        if (tf != ind) {
                            int comparison = this.comparer.compare(ind, tf);
                            switch (comparison) {
                                //case DOMINATING:
                                case 1: {
                                    vec2front.put(tf, -1);
                                    f.remove();
                                    break;
                                }
                                case -1: {
                                    vec2front.put(ind, -1);
                                    front.remove(ind);
                                    b_break = true;
                                    break;
                                }
                                case 0: {
                                    break;
                                }
                                default:
                                    throw new IllegalArgumentException("Unhandled: " + comparison);
                            }

                            if (b_break) {
                                break;
                            }
                        }
                    }
                }
            }

            if (front.isEmpty()) {
                break;
            }

            //	Move the individuals to temp
            temp.addAll(front);

            //	Remove them from individuals
            for (Iterator<VEC> it = vectors.iterator(); it.hasNext();) {
                if (front.contains(it.next())) {
                    it.remove();
                }
            }

            numUnranked -= front.size();

            front.clear();

            curRank++;
        }

        vectors.addAll(temp);

        for (VEC vector : vectors) {
            //if we're tracking less fronts than we have, we might not have
            //recorded this vector's pareto rank
            if (vec2front.containsKey(vector)) {
                int rank = vec2front.get(vector);
                if (rank == -1) {
                    vec2front.remove(vector);
                } else {
                    if (!front2vecs.containsKey(rank)) {
                        front2vecs.put(rank, new ArrayList<VEC>());
                    }
                    front2vecs.get(rank).add(vector);
                }
            }
        }

        temp.clear();
    }
}
